Skip to content

feat(button): improve badge spacing and stacking - #31647

Draft
thetaPC wants to merge 49 commits into
ionic-modularfrom
FW-6838-badge-spacing
Draft

thetaPC wants to merge 49 commits into
ionic-modularfrom
FW-6838-badge-spacing

Conversation

@thetaPC

@thetaPC thetaPC commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Issue number: resolves internal


What is the current behavior?

A badge without a vertical position sits against the button's label with no gap. In md and ios, a badge taller than the label also makes the button taller, so buttons of the same size have different heights. A badge with a vertical position that overflows its button can also be covered by the button next to it.

What is the new behavior?

  • A badge without a vertical position in a text button has a gap from the label, set by the new IonButton.size.<size>.badge.margin token: 8px, 10px and 12px in ionic, 8px, 8px and 12px in md, and 4px in ios, the same as each theme's icon gap
  • A button with a badge drops its vertical padding, so its height comes from min.height and a tall badge no longer grows it. --padding-top and --padding-bottom still override this
  • A button with a badge sits above the buttons beside it, so a badge that overflows its button stays visible instead of being covered by the next button
  • Icon-only buttons and the placement of badges with a vertical position don't change

Does this introduce a breaking change?

  • Yes
  • No

A badge without a vertical position now has a gap between it and the label, and no longer makes the button taller than its min.height. To remove the gap, set IonButton.size.<size>.badge.margin.start for global styles or --ion-button-size-<size>-badge-margin-start for component-specific styles to 0.

Other information

Known gaps

  • Two buttons that both have badges can still cover each other's badges. Each button is its own stacking context, so a badge can't rise above another button that also sits on top. A full fix means removing the two rules that create that context: contain: layout on the native element, added for the unbounded ripple in feat(ripple): adds unbounded ripple-effect #16399, and z-index: 1 on the inner element, added for a state overlay in feat(components): improve hover states for buttons #19440 that the migration removed. That changes how every button paints, including the ripple's layering and possibly its performance, and badges would then paint over other page content like sticky list dividers. It needs its own investigation and a full screenshot sweep, so it's tracked in FW-7920

How to test

Check Preview What to look for
Badges without a position ios, md, ionic In the "badge position: none" sections, text buttons have a gap before the badge, and every button in a row is the same height

The stacking case has no test page: every button on the badge page has a badge. The badge spec checks it instead ("should keep a badge above the next button"), and it fails without the fix.

thetaPC and others added 28 commits October 13, 2026 16:42
# Conflicts:
#	core/src/components/toolbar/test/colors/toolbar.e2e.ts-snapshots/toolbar-colors-ionic-md-ltr-light-Mobile-Safari-linux.png
@thetaPC
thetaPC added this pull request to stack #31629 October 23, 2026 22:58
@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
ionic-framework Ready Ready Preview Oct 26, 2026 6:18pm UTC

Request Review

@github-actions github-actions Bot added the package: core @ionic/core package label Oct 23, 2026
@thetaPC thetaPC mentioned this pull request Oct 23, 2026
1 of 2 tasks
Ionitron and others added 16 commits October 23, 2026 23:34
# Conflicts:
#	core/src/components/button/test/basic/index.html
# Conflicts:
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ionic-ios-ltr-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ionic-ios-ltr-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ionic-ios-ltr-light-Mobile-Safari-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ionic-ios-rtl-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ionic-ios-rtl-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ionic-ios-rtl-light-Mobile-Safari-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ionic-md-ltr-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ionic-md-ltr-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ionic-md-ltr-light-Mobile-Safari-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ionic-md-rtl-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ionic-md-rtl-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ionic-md-rtl-light-Mobile-Safari-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ios-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ios-ltr-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ios-ltr-Mobile-Safari-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ios-rtl-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ios-rtl-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ios-rtl-Mobile-Safari-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-ltr-Mobile-Safari-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-rtl-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-rtl-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-rtl-Mobile-Safari-linux.png
#	core/src/components/button/test/clear/button.e2e.ts-snapshots/button-fill-clear-ios-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/clear/button.e2e.ts-snapshots/button-fill-clear-ios-ltr-Mobile-Firefox-linux.png
#	core/src/components/button/test/clear/button.e2e.ts-snapshots/button-fill-clear-ios-ltr-Mobile-Safari-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ionic-ios-ltr-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ionic-ios-ltr-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ionic-ios-ltr-light-Mobile-Safari-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ionic-ios-rtl-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ionic-ios-rtl-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ionic-ios-rtl-light-Mobile-Safari-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ionic-md-ltr-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ionic-md-ltr-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ionic-md-ltr-light-Mobile-Safari-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ionic-md-rtl-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ionic-md-rtl-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ionic-md-rtl-light-Mobile-Safari-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ios-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ios-ltr-Mobile-Firefox-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ios-ltr-Mobile-Safari-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ios-rtl-Mobile-Chrome-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ios-rtl-Mobile-Firefox-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ios-rtl-Mobile-Safari-linux.png
…-sync-snapshots

# Conflicts:
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-ltr-Mobile-Safari-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-rtl-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-rtl-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-rtl-Mobile-Safari-linux.png
#	core/src/components/button/test/clear/button.e2e.ts-snapshots/button-fill-clear-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/clear/button.e2e.ts-snapshots/button-fill-clear-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/button/test/clear/button.e2e.ts-snapshots/button-fill-clear-md-ltr-Mobile-Safari-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-md-ltr-Mobile-Safari-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-md-rtl-Mobile-Chrome-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-md-rtl-Mobile-Firefox-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-md-rtl-Mobile-Safari-linux.png
…os-sync-snapshots

# Conflicts:
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ios-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ios-ltr-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ios-ltr-Mobile-Safari-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ios-rtl-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ios-rtl-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-ios-rtl-Mobile-Safari-linux.png
#	core/src/components/button/test/clear/button.e2e.ts-snapshots/button-fill-clear-ios-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/clear/button.e2e.ts-snapshots/button-fill-clear-ios-ltr-Mobile-Firefox-linux.png
#	core/src/components/button/test/clear/button.e2e.ts-snapshots/button-fill-clear-ios-ltr-Mobile-Safari-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ios-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ios-ltr-Mobile-Firefox-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ios-ltr-Mobile-Safari-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ios-rtl-Mobile-Chrome-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ios-rtl-Mobile-Firefox-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-ios-rtl-Mobile-Safari-linux.png
@thetaPC thetaPC changed the title feat(button): space badges without a position from the label feat(button): improve badge spacing and stacking Oct 26, 2026
Base automatically changed from FW-6838-ios-sync-snapshots to ionic-modular October 26, 2026 22:54

This branch was successfully deployed

1 active deployment
Preview — 15b2ee06 Deployed Oct 26, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: core @ionic/core package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants